package com.gjy.minio.service.imp;

import com.gjy.minio.convert.BucketConvert;
import com.gjy.minio.domain.BucketInfo;
import com.gjy.minio.domain.FileInfo;
import com.gjy.minio.domain.FileItem;
import com.gjy.minio.service.MinioService;
import com.google.common.collect.Lists;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Item;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * @author 宫静雨
 * @version 1.0
 * @since 2023-09-04 20:09:09
 */
@Service
public class MinioServiceImpl implements MinioService {

    private static final Logger log = LoggerFactory.getLogger(MinioServiceImpl.class);

    private static final String BUCKET_PARAM = "${bucket}";
    /**
     * bucket权限-读写, 可以生产永久可访问的路径
     */
    private static final String READ_WRITE = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:DeleteObject\",\"s3:GetObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\",\"s3:AbortMultipartUpload\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "/*\"]}]}";

    /**
     * bucket权限-只读, 只能生成最多7天的临时访问路径
     */
    private static final String WRITE_ONLY = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "/*\"]}]}";

    @Resource
    private MinioClient minio;
    @Resource
    private BucketConvert bucketConvert;

    @Override
    public boolean existsBucket(String bucket) {
        try {
            return minio.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
        } catch (Exception e) {
            log.info("判断bucket[{}] 失败: {}", bucket, e.getMessage());
            return false;
        }
    }

    @Override
    public boolean createBucket(String bucket, String type) {
        try {
            minio.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
            if (type.equals("READ_WRITE")) {
                minio.setBucketPolicy(SetBucketPolicyArgs.builder()
                        .bucket(bucket)
                        .config(READ_WRITE.replace(BUCKET_PARAM, bucket))
                        .build());
            } else {
                minio.setBucketPolicy(SetBucketPolicyArgs.builder()
                        .bucket(bucket)
                        .config(WRITE_ONLY.replace(BUCKET_PARAM, bucket))
                        .build());
            }
            return true;
        } catch (Exception e) {
            log.info("创建bucket[{}] 失败: {}", bucket, e.getMessage());
            return false;
        }
    }

    @Override
    public boolean deleteBucket(String bucket) {
        try {
            minio.removeBucket(RemoveBucketArgs.builder().bucket(bucket).build());
            return true;
        } catch (Exception e) {
            log.info("删除bucket[{}] 失败: {}", bucket, e.getMessage());
            return false;
        }
    }

    @Override
    public boolean deleteBucketForce(String bucket) {
        try {
            getFiles(bucket).stream().map(FileItem::getName).forEach(n -> {
                try {
                    minio.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(n).build());
                } catch (Exception e) {
                    log.error("删除bucket[{}], 删除文件[{}], 失败: {}", bucket, n, e.getMessage());
                }
            });
            minio.removeBucket(RemoveBucketArgs.builder().bucket(bucket).build());
            return true;
        } catch (Exception e) {
            log.info("删除bucket[{}] 失败: {}", bucket, e.getMessage());
            return false;
        }
    }

    @Override
    public List<BucketInfo> getBuckets() {
        try {
            return bucketConvert.convert(minio.listBuckets());
        } catch (Exception e) {
            log.error("获取bucket 失败: {}", e.getMessage());
            return Lists.newArrayList();
        }
    }

    @Override
    public FileInfo uploadFile(String bucket, MultipartFile file) {

        try {
            InputStream inputStream = file.getInputStream();
            String originalFilename = file.getOriginalFilename();
            long fileSize = file.getSize();

            String filename = UUID.randomUUID().toString().replaceAll("-", "");

            minio.putObject(PutObjectArgs.builder().bucket(bucket).object(filename).stream(inputStream, fileSize, -1).contentType(file.getContentType()).build());

            return previewFile(bucket, filename);
        } catch (Exception e) {
            return FileInfo.empty();
        }
    }

    @Override
    public List<FileInfo> uploadFile(String bucket, List<MultipartFile> files) {
        return files.stream().map(f -> uploadFile(bucket, f)).collect(Collectors.toList());
    }

    @Override
    public void downloadFile(String bucket, String filename, HttpServletResponse response) {
        try {
            GetObjectResponse object = minio.getObject(GetObjectArgs.builder()
                    .bucket(bucket)
                    .object(filename).build());
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];
            int len;
            while ((len = object.read(buf)) != -1) {
                baos.write(buf, 0, len);
            }
            baos.flush();
            byte[] bytes = baos.toByteArray();

            response.setHeader("Content-Disposition", "attachment;filename=" +
                    URLEncoder.encode(filename, StandardCharsets.UTF_8.name()));
            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            ServletOutputStream outputStream = response.getOutputStream();
            outputStream.write(bytes);
            outputStream.flush();
            outputStream.close();
            baos.close();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public FileInfo previewFile(String bucket, String filename) {
        try {
            String url = minio.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .bucket(bucket).object(filename).method(Method.GET).build());
            return new FileInfo(filename, url);
        } catch (Exception e) {
            log.error("{}", e.getMessage());
            return FileInfo.empty();
        }
    }

    @Override
    public boolean deleteFile(String bucket, String filename) {
        try {
            minio.removeObject(RemoveObjectArgs.builder()
                    .bucket(bucket)
                    .object(filename).build());
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public List<FileItem> getFiles(String bucket) {
        try {
            Iterable<Result<Item>> results = minio.listObjects(ListObjectsArgs.builder().recursive(true).bucket(bucket).build());
            List<FileItem> list = Lists.newArrayList();

            results.forEach(r -> {
                try {
                    Item item = r.get();
                    String name = item.objectName();
                    list.add(new FileItem(name));
                } catch (Exception e) {
                    log.error("获取文件失败: {}", e.getMessage());
                }
            });
            return list;
        } catch (Exception e) {
            return Lists.newArrayList();
        }

    }

}
